/* 
 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
 * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.at>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"
#include "stdio.h"

#include "../include/mb.h"
#include "../include/mbconfig.h"
#include "../include/mbframe.h"
#include "../include/mbport.h"
#include "mbascii.h"

#if MB_ASCII_ENABLED > 0

/* ----------------------- Defines ------------------------------------------*/
#define MB_ASCII_DEFAULT_CR     '\r'    /*!< Modbus ASCII的默认CR字符 */
#define MB_ASCII_DEFAULT_LF     '\n'    /*!< Modbus ASCII的默认LF字符 */
#define MB_SER_PDU_SIZE_MIN     3       /*!< Modbus ASCII帧的最小尺寸 */
#define MB_SER_PDU_SIZE_MAX     256     /*!< Modbus ASCII帧的最大尺寸 */
#define MB_SER_PDU_SIZE_LRC     1       /*!< PDU中LRC字段的大小 */
#define MB_SER_PDU_ADDR_OFF     0       /*!< Ser-PDU中从机地址的偏移量 */
#define MB_SER_PDU_PDU_OFF      1       /*!< 串行协议数据单元（Ser-PDU）中Modbus协议数据单元（Modbus-PDU）的偏移量 */

/* ----------------------- Type definitions ---------------------------------*/
typedef enum
{
    BYTE_HIGH_NIBBLE,           /*!< 字节高四位的字符 */
    BYTE_LOW_NIBBLE             /*!< 字节低半字节的字符 */
} eMBBytePos;

/* ----------------------- Static functions ---------------------------------*/
static uint8_t    prvucMBCHAR2BIN( uint8_t ucCharacter );
static uint8_t    prvucMBBIN2CHAR( uint8_t ucByte );
static uint8_t    prvucMBLRC( uint8_t * pucFrame, uint16_t usLen );

/* ----------------------- Static variables ---------------------------------*/
/* 我们复用Modbus RTU缓冲区，因为只需要一个缓冲区，而且RTU缓冲区更大。 */
extern uint8_t WHT_RTU_Buffer[MB_SER_PDU_SIZE_MAX];
static uint8_t WHT_ASCII_Buffer[MB_SER_PDU_SIZE_MAX*2+1];

/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode WHT_MB_ASCII_Init(uint8_t ucSlaveAddress)
{
    eMBErrorCode eStatus = MB_ENOERR;

    if(WHT_MB_Port_Serial_Init() != true )
    {
        eStatus = MB_EPORTERR;
    }
    return eStatus;
}
eMBErrorCode WHT_MB_ASCII_Receive(uint8_t* pucRcvAddress, uint8_t** pucFrame, uint16_t* pusLength)
{
    eMBErrorCode eStatus = MB_ENOERR;
    eMBBytePos eBytePos;
    uint16_t ASCII_Length;
    uint16_t usRcvBufferPos;

    /*获取ModBus_ASCII报文*/
    ASCII_Length = WHT_FreeModBus_Scanf(WHT_ASCII_Buffer, sizeof(WHT_ASCII_Buffer));
    /* 接收到一个新字符。如果该字符是 ':'，则输入缓冲区会被清空。回车符（CR）表示数据块的结束。其他字符属于数据块的一部分，它们的ASCII值会被转换回二进制表示形式 */
    if (WHT_ASCII_Buffer[0] == ':')
    {
        eBytePos = BYTE_HIGH_NIBBLE;
        ASCII_Length -= 1;
        usRcvBufferPos = 0;
    }
    else
        return MB_EIO;
    for (uint16_t i = 0; i < ASCII_Length; i++)
    {
        static volatile uint8_t* ASICC_Buffer = &WHT_ASCII_Buffer[1];

        if (ASICC_Buffer[i] == MB_ASCII_DEFAULT_CR)// MB_ASCII_DEFAULT_LF
            break;
        /* 字节的高四位在前。我们在此处检查缓冲区溢出。*/
        if (eBytePos == BYTE_HIGH_NIBBLE)
        {
            eBytePos = BYTE_LOW_NIBBLE;
            WHT_RTU_Buffer[i/2] = prvucMBCHAR2BIN(ASICC_Buffer[i]) << 4;
        }
        else if (eBytePos == BYTE_LOW_NIBBLE)
        {
            eBytePos = BYTE_HIGH_NIBBLE;
            WHT_RTU_Buffer[i/2] |= prvucMBCHAR2BIN(ASICC_Buffer[i]);
            usRcvBufferPos++;
        }
    }

    /* 长度和循环冗余校验检查 */
    if ((usRcvBufferPos >= MB_SER_PDU_SIZE_MIN) && (prvucMBLRC(WHT_RTU_Buffer, usRcvBufferPos) == 0 ))
    {
        /* 保存地址字段。所有帧都传递到上层，关于是否使用某一帧的决定在那里做出。*/
        *pucRcvAddress = WHT_RTU_Buffer[MB_SER_PDU_ADDR_OFF];

        /* Modbus协议数据单元（Modbus-PDU）的总长度等于Modbus串行线路协议数据单元（Modbus-Serial-Line-PDU）减去地址字段和循环冗余校验（CRC）的大小。*/
        *pusLength = (uint16_t)( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );

        /* 向调用者返回Modbus协议数据单元（PDU）的起始部分。 */
        *pucFrame = (uint8_t*)&WHT_RTU_Buffer[MB_SER_PDU_PDU_OFF];
    }
    else
    {
        printf("MB ASCII LRC ERROR!!!\r\n");
        printf("LRC=0x%02x\r\n", prvucMBLRC(WHT_RTU_Buffer, usRcvBufferPos - MB_SER_PDU_SIZE_LRC));
        eStatus = MB_EIO;
    }
    return eStatus;
}
eMBErrorCode WHT_MB_ASCII_Send(uint8_t ucSlaveAddress, const uint8_t* pucFrame, uint16_t usLength)
{
    static uint8_t *pucSndBufferCur;
    static uint16_t usSndBufferCount;
    eMBBytePos eBytePos;

    /* Modbus协议数据单元（Modbus-PDU）前的第一个字节是从站地址。 */
    pucSndBufferCur = ( uint8_t * ) pucFrame - 1;
    usSndBufferCount = 1;

    /* 现在将Modbus协议数据单元（Modbus-PDU）复制到Modbus串行线路协议数据单元（Modbus-Serial-Line-PDU）中。 */
    pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
    usSndBufferCount += usLength;

    /* 计算Modbus串行线路协议数据单元（Modbus-Serial-Line-PDU）的纵向冗余校验（LRC）校验和。 */
    WHT_ASCII_Buffer[usSndBufferCount++] = prvucMBLRC((uint8_t*)pucSndBufferCur, usSndBufferCount);

    /* 传输开始。帧的起始通过发送字符“:”来定义。 */
    WHT_ASCII_Buffer[0] = ':';
    eBytePos = BYTE_HIGH_NIBBLE;
    /* 发送数据块。每个数据字节都被编码为一个字符十六进制流，高四位先发送，低四位后发送。如果所有数据字节都已发送完毕，我们会发送一个 '\r' 字符来结束传输。*/
    usSndBufferCount = (usSndBufferCount -1) * 2;
    uint8_t i;
    for (i = 1; i <= usSndBufferCount; i++)
    {
        if (eBytePos == BYTE_HIGH_NIBBLE)
        {
            eBytePos = BYTE_LOW_NIBBLE;
            WHT_ASCII_Buffer[i] = prvucMBBIN2CHAR((uint8_t)(*pucSndBufferCur >> 4));
        }
        else if (eBytePos == BYTE_LOW_NIBBLE)
        {
            eBytePos = BYTE_HIGH_NIBBLE;
            WHT_ASCII_Buffer[i] = prvucMBBIN2CHAR((uint8_t)(*pucSndBufferCur & 0x0F));
            pucSndBufferCur++;
        }
    }
    WHT_ASCII_Buffer[i++] = MB_ASCII_DEFAULT_CR;
    WHT_ASCII_Buffer[i++] = MB_ASCII_DEFAULT_LF;
    /* 启动发送传输 */
    WHT_FreeModBus_Printf(WHT_ASCII_Buffer, usSndBufferCount + 1 + 2);// : \r \n
    return MB_ENOERR;
}

static uint8_t prvucMBCHAR2BIN(uint8_t ucCharacter)
{
    if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )
    {
        return ( uint8_t )( ucCharacter - '0' );
    }
    else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )
    {
        return ( uint8_t )( ucCharacter - 'A' + 0x0A );
    }
    else
    {
        return 0xFF;
    }
}
static uint8_t prvucMBBIN2CHAR(uint8_t ucByte)
{
    if( ucByte <= 0x09 )
    {
        return ( uint8_t )( '0' + ucByte );
    }
    else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )
    {
        return ( uint8_t )( ucByte - 0x0A + 'A' );
    }
    else
    {
        /* 编程错误 */
    }
    return '0';
}
static uint8_t prvucMBLRC(uint8_t* pucFrame, uint16_t usLen)
{
    uint8_t ucLRC = 0;  /* LRC字符已初始化 */

    while( usLen-- )
    {
        ucLRC += *pucFrame++;   /* 无进位添加缓冲字节 */
    }
    /* 返回二进制补码 */
    ucLRC = ( uint8_t ) ( -( ( char ) ucLRC ) );
    return ucLRC;
}

#endif
